﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;

namespace TeamFoundation.ContinuousMerge.BranchGraph
{
    /// <summary>
    /// Represents a <see cref="BranchPackageBuilder"/> with a full default chain.
    /// </summary>
    public class FullDefaultBranchPackageBuilder : DefaultBranchPackageBuilder<DefaultBranchGraph, BranchNode>
    { 
        /// <summary>
        /// Creates a new instance of the <see cref="FullDefaultBranchPackageBuilder"/>
        /// class.
        /// </summary>
        public FullDefaultBranchPackageBuilder()
        {
        }
    }

    /// <summary>
    /// Represents a <see cref="BranchPackageBuilder"/> the builds to a 
    /// <see cref="BranchPackage"/>.
    /// </summary>
    public class DefaultBranchPackageBuilder<TGraph, TNode> : BranchPackageBuilder<BranchPackage<TGraph, TNode>, TGraph, TNode>
        where TGraph : BranchGraph<TNode>, new()
        where TNode : BranchNode, new()
    {
        /// <summary>
        /// Creates a new instance of the <see cref="DefaultBranchPackageBuilder"/>
        /// class.
        /// </summary>
        public DefaultBranchPackageBuilder()
        {
        }
    }
    
    /// <summary>
    /// Represents a branch graph builder.
    /// </summary>
    /// <typeparam name="TPackage">The type of package to build.</typeparam>
    public class BranchPackageBuilder<TPackage, TGraph, TNode> 
        where TPackage : BranchPackage<TGraph, TNode>, new()
        where TGraph : BranchGraph<TNode>, new()
        where TNode : BranchNode, new()
    {
        /// <summary>
        /// List of nodes we are interested in.
        /// </summary>
        private List<ItemSpec> _interests = new List<ItemSpec>();

        /// <summary>
        /// Creates a new instance of the <see cref="BranchGraphBuilder"/>
        /// class.
        /// </summary>
        public BranchPackageBuilder()
        {
        }

        /// <summary>
        /// Adds a node of interest.
        /// </summary>
        /// <param name="serverPath">The server path for the node.</param>
        public void AddInterest(string serverPath)
        {
            _interests.Add(new ItemSpec(serverPath, RecursionType.None));
        }

        /// <summary>
        /// Generates the final node graph.
        /// </summary>
        /// <returns>A <see cref="BranchNode"/> that represents <see cref="Root"/>.</returns>
        public TPackage Generate(TeamFoundationServer tfs)
        {
            var vcs = tfs.GetService<VersionControlServer>();

            var specs = _interests.ToArray();
            var bh = vcs.GetBranchHistory(specs, VersionSpec.Latest);
            var packageData = new TPackage();

            // Build custom stack.
            var items = new Stack<BranchHistoryTreeItem>();
            foreach (var interestHistory in bh)
            {
                foreach (var item in interestHistory)
                {
                    items.Push(item);
                }
            }

            // Get items.
            while (items.Count > 0)
            {
                var item = items.Pop();

                string from = "$/";
                string to = "$/";

                if (item.Relative.BranchFromItem != null)
                {
                    from = item.Relative.BranchFromItem.ServerItem;
                }
                if (item.Relative.BranchToItem != null)
                {
                    to = item.Relative.BranchToItem.ServerItem;
                }

                // Prevent infinte recursion.
                if (packageData.AddFlyweight(from, to, () => new List<string>()))
                {
                    foreach (BranchHistoryTreeItem childItem in item.Children)
                    {
                        items.Push(childItem);
                    }
                }
            }

            return packageData;
        }
    }
}
